package com.closer.codebox.biz.cpp;

import com.closer.codebox.biz.CommonTemplate;
import com.closer.codebox.biz.MemoryMonitorService;
import com.closer.codebox.support.dto.ExecuteCodeRequest;
import com.closer.codebox.support.dto.JudgeCase;
import com.closer.codebox.support.dto.JudgeConfig;
import com.closer.codebox.support.dto.JudgeInfo;
import com.closer.codebox.support.enums.JudgeInfoMessageEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class CppCodeSandbox extends CommonTemplate {

    private final MemoryMonitorService memoryMonitorService;

    public CppCodeSandbox(MemoryMonitorService memoryMonitorService) {
        this.memoryMonitorService = memoryMonitorService;
    }
    public JudgeInfo executeCode(ExecuteCodeRequest params) {
        try {
            var code = params.getCode();

            // 替换非标准头文件
            code = replaceNonStandardHeaders(code);

            // 1. 保存代码文件
            var userCodeFile = saveCodeToFile(code, "main.cpp", "cpp");
            // 2. 编译代码
            var compileResult = compileFile(userCodeFile);
            if (compileResult != null) {
                cleanTempFiles(userCodeFile);
                return JudgeInfo.builder()
                        .message(JudgeInfoMessageEnum.COMPILE_ERROR.getValue())
                        .memory(0L)
                        .time(0L)
                        .build();
            }

            // 3. 运行代码
            var result = runFile(userCodeFile, params.getTestCases(), params.getJudgeConfig());

            cleanTempFiles(userCodeFile);
            return result;

        } catch (Exception e) {
            log.error("执行C++代码时发生异常: {}", e.getMessage());
            return JudgeInfo.builder()
                    .message(JudgeInfoMessageEnum.SYSTEM_ERROR.getValue())
                    .memory(0L)
                    .time(0L)
                    .build();
        }
    }

    /**
     * 替换非标准的C++头文件
     */
    private String replaceNonStandardHeaders(String code) {
        // 替换 bits/stdc++.h 为常用标准头文件组合
        if (code.contains("#include <bits/stdc++.h>")) {
            log.info("检测到非标准头文件 bits/stdc++.h，进行替换");

            String standardHeaders = """
                #include <iostream>
                #include <vector>
                #include <string>
                #include <algorithm>
                #include <cmath>
                #include <map>
                #include <set>
                #include <queue>
                #include <stack>
                #include <functional>
                #include <cstdio>
                #include <cstdlib>
                #include <cstring>
                """;

            code = code.replace("#include <bits/stdc++.h>", standardHeaders);
        }
        return code;
    }

    /**
     * 编译C++文件
     */
    public String compileFile(File file) {
        var executableName = getExecutableName(file);
        var executablePath = new File(file.getParentFile(), executableName).getAbsolutePath();

        var compileCmd = String.format("g++ -std=c++17 -O2 -Wall -Wextra %s -o %s",
                file.getAbsolutePath(), executablePath);

        try {
            var processBuilder = new ProcessBuilder();
            var isWindows = System.getProperty("os.name").toLowerCase().contains("windows");

            if (isWindows) {
                processBuilder.command("cmd.exe", "/c", compileCmd);
            } else {
                processBuilder.command("sh", "-c", compileCmd);
            }

            processBuilder.directory(file.getParentFile());
            processBuilder.redirectErrorStream(true);

            var process = processBuilder.start();

            // 读取编译输出
            var output = new StringBuilder();
            try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    output.append(line).append("\n");
                }
            }

            var finished = process.waitFor(30, TimeUnit.SECONDS);
            if (!finished) {
                process.destroyForcibly();
                return "编译超时：超过30秒未完成";
            }

            if (process.exitValue() == 0) {
                return null;
            } else {
                var errorMessage = output.toString();
                return "编译错误：" + errorMessage;
            }
        } catch (Exception e) {
            return "编译异常：" + e.getMessage();
        }
    }

    /**
     * 运行C++可执行文件
     */
    public JudgeInfo runFile(File file, List<JudgeCase> testCases, JudgeConfig judgeConfig) {
        long maxTime = 0L;
        long maxMemory = 0L;

        var executableName = getExecutableName(file);
        var executableFile = new File(file.getParentFile(), executableName);

        if (!executableFile.exists()) {
            return JudgeInfo.builder()
                    .message(JudgeInfoMessageEnum.COMPILE_ERROR.getValue())
                    .memory(0L)
                    .time(0L)
                    .build();
        }

        for (var testCase : testCases) {
            var input = testCase.getInput();
            var expectedOutput = testCase.getOutput();
            Process process = null;

            try {
                var startTime = System.currentTimeMillis();
                var command = new ArrayList<String>();
                command.add(executableFile.getAbsolutePath());

                var processBuilder = new ProcessBuilder(command);
                processBuilder.directory(file.getParentFile());
                processBuilder.redirectErrorStream(true);

                process = processBuilder.start();

                // 内存监控
                final long finalPid = process.pid();
                var memoryFuture = CompletableFuture.supplyAsync(() ->
                        memoryMonitorService.monitorPeakMemory(finalPid, judgeConfig.getTimeLimit() + 1000)
                );

                // 写入输入
                if (input != null && !input.isEmpty()) {
                    try (var writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()))) {
                        writer.write(input);
                        writer.flush();
                    }
                }

                // 读取输出
                var outputBuilder = new StringBuilder();
                try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        outputBuilder.append(line);
                        if (reader.ready()) {
                            outputBuilder.append("\n");
                        }
                    }
                }

                // 等待进程完成
                var finished = process.waitFor(judgeConfig.getTimeLimit(), TimeUnit.MILLISECONDS);
                var endTime = System.currentTimeMillis();
                var executionTime = endTime - startTime;

                var peakMemoryUsage = memoryFuture.get(judgeConfig.getTimeLimit() + 2000, TimeUnit.MILLISECONDS);

                if (!finished) {
                    process.destroyForcibly();
                    return JudgeInfo.builder()
                            .message(JudgeInfoMessageEnum.TIME_LIMIT_EXCEEDED.getValue())
                            .time(maxTime)
                            .memory(maxMemory)
                            .build();
                }

                // 检查退出代码
                int exitCode = process.exitValue();
                if (exitCode != 0) {
                    return JudgeInfo.builder()
                            .message(JudgeInfoMessageEnum.RUNTIME_ERROR.getValue())
                            .time(executionTime)
                            .memory(peakMemoryUsage)
                            .build();
                }

                // 更新最大时间和内存
                maxTime = Math.max(maxTime, executionTime);
                maxMemory = Math.max(maxMemory, peakMemoryUsage);

                // 检查内存限制
                if (maxMemory > judgeConfig.getMemoryLimit() * 1024) {
                    return JudgeInfo.builder()
                            .message(JudgeInfoMessageEnum.MEMORY_LIMIT_EXCEEDED.getValue())
                            .time(maxTime)
                            .memory(maxMemory)
                            .build();
                }

                var actualOutput = outputBuilder.toString().trim();
                log.info("C++测试用例 - 输入: [{}], 预期输出: [{}], 实际输出: [{}]",
                        input, expectedOutput, actualOutput);

                if (!actualOutput.equals(expectedOutput)) {
                    return JudgeInfo.builder()
                            .message(JudgeInfoMessageEnum.WRONG_ANSWER.getValue())
                            .time(maxTime)
                            .memory(maxMemory)
                            .build();
                }

            } catch (Exception e) {
                if (process != null && process.isAlive()) {
                    process.destroyForcibly();
                }
                log.error("执行C++测试用例时发生错误: {}", e.getMessage());
                return JudgeInfo.builder()
                        .message(JudgeInfoMessageEnum.RUNTIME_ERROR.getValue())
                        .time(maxTime)
                        .memory(maxMemory)
                        .build();
            }
        }

        return JudgeInfo.builder()
                .message(JudgeInfoMessageEnum.ACCEPTED.getValue())
                .time(maxTime)
                .memory(maxMemory)
                .build();
    }

    /**
     * 获取可执行文件名
     */
    private String getExecutableName(File file) {
        var baseName = file.getName().replace(".cpp", "");
        var isWindows = System.getProperty("os.name").toLowerCase().contains("windows");
        return isWindows ? baseName + ".exe" : baseName;
    }
}